modernize garmin_txt (#1080)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Sun, 23 Apr 2023 21:25:58 +0000 (15:25 -0600)
committerGitHub <noreply@github.com>
Sun, 23 Apr 2023 21:25:58 +0000 (15:25 -0600)
* modernize garmin_txt string handling.

also:
wean from strupper.
test binding fields to the headers in the reader.
test the writer.

* demacro garmin_txt

* eliminate redundant assignments

* astyle

* use containers for garmin_txt mapping info.

* garmin_txt reader warn on unrecognized header

and retire memset.

* use some bools, change parse_date_and_time signature.

garmin_txt.cc
reference/garmin_txt_reorder.txt [new file with mode: 0755]
testo.d/garmin_txt.test

index 9146aa6dec083c36b30be557d3016aacff59106e..c78be4a364b0b0aa2dcf54b00fcd666b75cc288a 100644 (file)
 #include "defs.h"
 
 #if CSVFMTS_ENABLED
-#include <algorithm>               // for sort
+#include <algorithm>               // for for_each, sort
+#include <array>                   // for array
 #include <cctype>                  // for toupper
 #include <cmath>                   // for fabs, floor
-#include <cstdio>                  // for NULL, snprintf, sscanf
-#include <cstdint>
+#include <cstdint>                 // for uint16_t
+#include <cstdio>                  // for sscanf, fprintf, snprintf, stderr
 #include <cstdlib>                 // for abs
-#include <cstring>                 // for memset, strstr, strcat, strchr, strlen, strcmp, strcpy, strncpy
-#include <ctime>                   // for gmtime, localtime, strftime
+#include <cstring>                 // for strstr, strlen
+#include <ctime>                   // for time_t, gmtime, localtime, strftime
+#include <utility>                 // for pair, make_pair
 
 #include <QByteArray>              // for QByteArray
 #include <QChar>                   // for QChar, QChar::Other_Control
+#include <QDateTime>               // for QDateTime
 #include <QIODevice>               // for QIODevice, QIODevice::ReadOnly, QIODevice::WriteOnly
+#include <QList>                   // for QList, QList<>::const_iterator
 #include <QString>                 // for QString, operator!=
 #include <QStringList>             // for QStringList
 #include <QTextStream>             // for QTextStream
@@ -52,7 +56,6 @@
 #include "src/core/datetime.h"     // for DateTime
 #include "src/core/logging.h"      // for Fatal
 #include "src/core/textstream.h"   // for TextStream
-#include "strptime.h"              // for strptime
 
 
 #define MYNAME "garmin_txt"
@@ -68,7 +71,8 @@ struct gtxt_flags_t {
 
 static gpsbabel::TextStream* fin = nullptr;
 static gpsbabel::TextStream* fout = nullptr;
-static route_head* current_trk, *current_rte;
+static route_head* current_trk;
+static route_head* current_rte;
 static int waypoints;
 static int routepoints;
 static const Waypoint** wpt_a;
@@ -79,7 +83,6 @@ static const char* datum_str;
 static int current_line;
 static char* date_time_format = nullptr;
 static int precision = 3;
-static QString current_line_text;
 static time_t utc_offs = 0;
 static gtxt_flags_t gtxt_flags;
 
@@ -114,19 +117,17 @@ inline gt_display_modes_e operator++(gt_display_modes_e& s, int) // postfix
   return ret;
 }
 
-#define MAX_HEADER_FIELDS 36
+static std::array<QList<std::pair<QString, int>>, unknown_header> header_mapping_info;
+static QStringList header_column_names;
 
-static QString header_lines[unknown_header + 1][MAX_HEADER_FIELDS];
-static int header_fields[unknown_header + 1][MAX_HEADER_FIELDS];
-static int header_ct[unknown_header + 1];
+static constexpr double kGarminUnknownAlt = 1.0e25;
+static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy";
+static constexpr char kDefaultTimeFormat[] = "HH:mm:ss";
 
-#define GARMIN_UNKNOWN_ALT 1.0e25f
-#define DEFAULT_DATE_FORMAT "dd/mm/yyyy"
-#define DEFAULT_TIME_FORMAT "HH:mm:ss"
-
-/* macros */
-
-#define IS_VALID_ALT(a) (((a) != unknown_alt) && ((a) < GARMIN_UNKNOWN_ALT))
+static inline bool is_valid_alt(double alt)
+{
+  return (alt != unknown_alt) && (alt < kGarminUnknownAlt);
+}
 
 static char* opt_datum = nullptr;
 static char* opt_dist = nullptr;
@@ -149,8 +150,9 @@ QVector<arglist_t> garmin_txt_args = {
   {"utc",   &opt_utc,         "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr},
 };
 
-class PathInfo {
- public:
+class PathInfo
+{
+public:
   double length {0};
   time_t start {0};
   time_t time {0};
@@ -166,15 +168,14 @@ static PathInfo* route_info;
 static int route_idx;
 static PathInfo* cur_info;
 
-static const char* headers[] = {
+static const QVector<QString> headers = {
   "Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t"
   "Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t"
   "Date Modified\tLink\tCategories",
   "Waypoint Name\tDistance\tLeg Length\tCourse",
   "Position\tTime\tAltitude\tDepth\tTemperature\tLeg Length\tLeg Time\tLeg Speed\tLeg Course",
   "Name\tLength\tCourse\tWaypoints\tLink",
-  "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink",
-  nullptr
+  "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink"
 };
 
 /* helpers */
@@ -192,10 +193,10 @@ init_date_and_time_format()
   // This is old, and weird, code.. date_time_format is a global that's
   // explicitly malloced and freed elsewhere. This isn't very C++ at all,
   // but this format is on its deathbead for deprecation.
-  const char* d = get_option_val(opt_date_format, DEFAULT_DATE_FORMAT);
+  const char* d = get_option_val(opt_date_format, kDefaultDateFormat);
   QString d1 = convert_human_date_format(d);
 
-  const char* t = get_option_val(opt_time_format, DEFAULT_TIME_FORMAT);
+  const char* t = get_option_val(opt_time_format, kDefaultTimeFormat);
   QString t1 = convert_human_time_format(t);
 
   xasprintf(&date_time_format, "%s %s", CSTR(d1), CSTR(t1));
@@ -464,9 +465,9 @@ print_distance(const double distance, const bool no_scale, const bool with_tab,
 }
 
 static void
-print_speed(const double* distance, const time_t* time)
+print_speed(const double distance, const time_t time)
 {
-  double dist = *distance;
+  double dist = distance;
   const char* unit;
 
   if (!gtxt_flags.metric) {
@@ -477,8 +478,8 @@ print_speed(const double* distance, const time_t* time)
   }
   int idist = qRound(dist);
 
-  if ((*time != 0) && (idist > 0)) {
-    double speed = MPS_TO_KPH(dist / (double)*time);
+  if ((time != 0) && (idist > 0)) {
+    double speed = MPS_TO_KPH(dist / (double)time);
     int ispeed = qRound(speed);
 
     if (speed < 0.01) {
@@ -535,7 +536,7 @@ write_waypt(const Waypoint* wpt)
   }
   const char* dspl_mode = gt_display_mode_names[i];
 
-  unsigned char wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0);
+  int wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0);
   if (wpt_class <= gt_waypt_class_map_line) {
     wpt_type = gt_waypt_class_names[wpt_class];
   } else {
@@ -560,7 +561,7 @@ write_waypt(const Waypoint* wpt)
 
   print_position(wpt);
 
-  if IS_VALID_ALT(wpt->altitude) {
+  if (is_valid_alt(wpt->altitude)) {
     print_distance(wpt->altitude, true, false, 0);
   }
   *fout << "\t";
@@ -620,7 +621,7 @@ route_disp_hdr_cb(const route_head* rte)
 
   if (!gtxt_flags.route_header_written) {
     gtxt_flags.route_header_written = 1;
-    *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[route_header]);
+    *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[route_header]);
   }
   print_string("\r\nRoute\t%s\t", rte->rte_name);
   print_distance(cur_info->length, false, true, 0);
@@ -631,7 +632,7 @@ route_disp_hdr_cb(const route_head* rte)
   } else {
     print_string("%s\r\n", "");
   }
-  *fout << QString::asprintf("\r\nHeader\t%s\r\n\r\n", headers[rtept_header]);
+  *fout << QStringLiteral("\r\nHeader\t%1\r\n\r\n").arg(headers[rtept_header]);
 }
 
 static void
@@ -674,19 +675,19 @@ track_disp_hdr_cb(const route_head* track)
 
   if (!gtxt_flags.track_header_written) {
     gtxt_flags.track_header_written = 1;
-    *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[track_header]);
+    *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[track_header]);
   }
   print_string("\r\nTrack\t%s\t", track->rte_name);
   print_date_and_time(cur_info->start, false);
   print_date_and_time(cur_info->time, true);
   print_distance(cur_info->length, false, true, 0);
-  print_speed(&cur_info->length, &cur_info->time);
+  print_speed(cur_info->length, cur_info->time);
   if (track->rte_urls.HasUrlLink()) {
     print_string("%s", track->rte_urls.GetUrlLink().url_);
   } else {
     print_string("%s", "");
   }
-  *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n\r\n", headers[trkpt_header]);
+  *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n\r\n").arg(headers[trkpt_header]);
 }
 
 static void
@@ -706,7 +707,7 @@ track_disp_wpt_cb(const Waypoint* wpt)
 
   print_position(wpt);
   print_date_and_time(wpt->GetCreationTime().toTime_t(), false);
-  if IS_VALID_ALT(wpt->altitude) {
+  if (is_valid_alt(wpt->altitude)) {
     print_distance(wpt->altitude, true, false, 0);
   }
 
@@ -727,7 +728,7 @@ track_disp_wpt_cb(const Waypoint* wpt)
     dist = waypt_distance_ex(prev, wpt);
     print_distance(dist, false, true, 0);
     print_date_and_time(delta, true);
-    print_speed(&dist, &delta);
+    print_speed(dist, delta);
     print_course(prev, wpt);
   }
   *fout << "\r\n";
@@ -742,7 +743,7 @@ track_disp_wpt_cb(const Waypoint* wpt)
 static void
 garmin_txt_wr_init(const QString& fname)
 {
-  memset(&gtxt_flags, 0, sizeof(gtxt_flags));
+  gtxt_flags = {};
 
   fout = new gpsbabel::TextStream;
   fout->open(fname, QIODevice::WriteOnly, MYNAME, "windows-1252");
@@ -823,7 +824,7 @@ garmin_txt_write()
 
   if (waypoints > 0) {
     wpt_a_ct = 0;
-    wpt_a = new const Waypoint*[waypoints]{};
+    wpt_a = new const Waypoint*[waypoints] {};
     waypt_disp_all(enum_waypt_cb);
     route_disp_all(nullptr, nullptr, enum_waypt_cb);
     auto sort_waypt_lambda = [](const Waypoint* wa, const Waypoint* wb)->bool {
@@ -831,7 +832,7 @@ garmin_txt_write()
     };
     std::sort(wpt_a, wpt_a + waypoints, sort_waypt_lambda);
 
-    *fout << QString::asprintf("Header\t%s\r\n\r\n", headers[waypt_header]);
+    *fout << QStringLiteral("Header\t%1\r\n\r\n").arg(headers[waypt_header]);
     for (int i = 0; i < waypoints; i++) {
       write_waypt(wpt_a[i]);
     }
@@ -866,13 +867,10 @@ garmin_txt_write()
 /* helpers */
 
 static void
-free_header(const header_type ht)
+free_headers()
 {
-  for (int i = 0; i < MAX_HEADER_FIELDS; i++) {
-    header_lines[ht][i].clear();
-  }
-  header_ct[ht] = 0;
-  memset(header_fields[ht], 0, sizeof(header_fields[ht]));
+  std::for_each(header_mapping_info.begin(), header_mapping_info.end(),
+                [](auto& list)->void { list.clear(); });
 }
 
 // Super simple attempt to convert strftime/strptime spec to Qt spec.
@@ -890,21 +888,51 @@ strftime_to_timespec(const char* s)
     case '%':
       if (i < l-1) {
         switch (s[++i]) {
-        case 'd': q += "dd"; continue;
-        case 'm': q += "MM"; continue ;
-        case 'y': q += "yy"; continue ;
-        case 'Y': q += "yyyy"; continue ;
-        case 'H': q += "hh"; continue ;
-        case 'M': q += "mm"; continue ;
-        case 'S': q += "ss"; continue ;
-        case 'A': q += "dddd"; continue ;
-        case 'a': q += "ddd"; continue ;
-        case 'B': q += "MMMM"; continue ;
-        case 'C': q += "yy"; continue ;
-        case 'D': q += "MM/dd/yyyy"; continue ;
-        case 'T': q += "hh:mm:ss"; continue ;
-        case 'F': q += "yyyy-MM-dd"; continue ;
-        default: q += s[i+1]; break ;
+        case 'd':
+          q += "dd";
+          continue;
+        case 'm':
+          q += "MM";
+          continue;
+        case 'y':
+          q += "yy";
+          continue;
+        case 'Y':
+          q += "yyyy";
+          continue;
+        case 'H':
+          q += "hh";
+          continue;
+        case 'M':
+          q += "mm";
+          continue;
+        case 'S':
+          q += "ss";
+          continue;
+        case 'A':
+          q += "dddd";
+          continue;
+        case 'a':
+          q += "ddd";
+          continue;
+        case 'B':
+          q += "MMMM";
+          continue;
+        case 'C':
+          q += "yy";
+          continue;
+        case 'D':
+          q += "MM/dd/yyyy";
+          continue;
+        case 'T':
+          q += "hh:mm:ss";
+          continue;
+        case 'F':
+          q += "yyyy-MM-dd";
+          continue;
+        default:
+          q += s[i+1];
+          break;
         }
       }
       break;
@@ -919,18 +947,11 @@ strftime_to_timespec(const char* s)
 
 /* data parsers */
 
-// This could return an optional QDateTime instead or a pair.
-static bool
-parse_date_and_time(const QString& str, QDateTime* value)
+static QDateTime
+parse_date_and_time(const QString& str)
 {
   QString timespec = strftime_to_timespec(date_time_format);
-  QDateTime dt;
-  dt = QDateTime::fromString(QString(str).trimmed(), timespec);
-  bool dt_is_valid = dt.isValid();
-  if (dt_is_valid) {
-    *value = dt;
-  }
-  return dt_is_valid;
+  return QDateTime::fromString(QString(str).trimmed(), timespec);
 }
 
 static uint16_t
@@ -985,18 +1006,9 @@ parse_temperature(const QString& str, double* temperature)
 static void
 parse_header(const QStringList& lineparts)
 {
-  int column = -1;
-
-  free_header(unknown_header);
-
-  for (const auto& str : lineparts) {
-    column++;
-    header_lines[unknown_header][column] = str;
-    header_lines[unknown_header][column] = header_lines[unknown_header][column].toUpper();
-    header_ct[unknown_header]++;
-    if (header_ct[unknown_header] >= MAX_HEADER_FIELDS) {
-      break;
-    }
+  header_column_names.clear();
+  for (const auto& name : lineparts) {
+    header_column_names.append(name.toUpper());
   }
 }
 
@@ -1024,42 +1036,30 @@ bind_fields(const header_type ht)
     fatal(MYNAME ": Incomplete or invalid file header!");
   }
 
-  if (header_ct[unknown_header] <= 0) {
+  if (header_column_names.isEmpty()) {
     return;
   }
-  free_header(ht);
+  header_mapping_info[ht].clear();
 
-  /* make a copy of headers[ht], uppercase, replace "\t" with "\0" */
+  /* make a copy of headers[ht], uppercase, split on "\t" */
+  const QStringList altheader = headers.at(ht).toUpper().split('\t');
 
-  int i = strlen(headers[ht]);
-  char* fields = (char*) xmalloc(i + 2);
-  strcpy(fields, headers[ht]);
-  strcat(fields, "\t");
-  char* c = strupper(fields);
-  while ((c = strchr(c, '\t'))) {
-    *c++ = '\0';
-  }
+  int i = -1;
+  for (const auto& name : qAsConst(header_column_names)) {
+    i++;
 
-  for (i = 0; i < header_ct[unknown_header]; i++) {
-    auto name = header_lines[ht][i] = header_lines[unknown_header][i];
-    header_lines[unknown_header][i].clear();
-
-    c = fields;
-    int field_no = 1;
-    while (*c) {
-      if (name.compare(c) == 0) {
-        header_fields[ht][i] = field_no;
-#if 0
-        printf("Binding field \"%s\" to internal number %d (%d,%d)\n", name, field_no, ht, i);
-#endif
-        break;
+    int field_idx = altheader.indexOf(name);
+    if (field_idx >= 0) {
+      int field_no = field_idx + 1;
+      header_mapping_info[ht].append(std::make_pair(name, field_no));
+      if (global_opts.debug_level >= 2) {
+        fprintf(stderr, MYNAME ": Binding field \"%s\" to internal number %d (%d,%d)\n", qPrintable(name), field_no, ht, i);
       }
-      field_no++;
-      c = c + strlen(c) + 1;
+    } else {
+      warning(MYNAME ": Field %s not recognized!\n", qPrintable(name));
     }
   }
-  header_ct[unknown_header] = 0;
-  xfree(fields);
+  header_column_names.clear();
 }
 
 static void
@@ -1105,10 +1105,13 @@ parse_waypoint(const QStringList& lineparts)
   wpt->fs.FsChainAdd(gmsd);
 
   for (const auto& str : lineparts) {
-    column++;
+    if (++column >= header_mapping_info[waypt_header].size()) {
+      warning(MYNAME ": too many fields in Waypoint record!\n");
+      break;
+    }
     int i;
     double d;
-    int field_no = header_fields[waypt_header][column];
+    const auto& [name, field_no] = header_mapping_info[waypt_header].at(column);
 
     switch (field_no) {
     case  1:
@@ -1178,12 +1181,10 @@ parse_waypoint(const QStringList& lineparts)
       garmin_fs_t::set_country(gmsd, str);
       garmin_fs_t::set_cc(gmsd, gt_get_icao_cc(str, wpt->shortname));
       break;
-    case 16: {
-      QDateTime ct;
-      if (parse_date_and_time(str, &ct)) {
-        wpt->SetCreationTime(ct);
+    case 16:
+      if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+        wpt->SetCreationTime(dt);
       }
-    }
     break;
     case 17: {
       wpt->AddUrlLink(str);
@@ -1208,8 +1209,11 @@ parse_route_header(const QStringList& lineparts)
 
   bind_fields(route_header);
   for (const auto& str : lineparts) {
-    column++;
-    int field_no = header_fields[route_header][column];
+    if (++column >= header_mapping_info[route_header].size()) {
+      warning(MYNAME ": too many fields in Route record!\n");
+      break;
+    }
+    const auto& [name, field_no] = header_mapping_info[route_header].at(column);
     switch (field_no) {
     case 1:
       if (!str.isEmpty()) {
@@ -1233,8 +1237,11 @@ parse_track_header(const QStringList& lineparts)
   bind_fields(track_header);
   auto* trk = new route_head;
   for (const auto& str : lineparts) {
-    column++;
-    int field_no = header_fields[track_header][column];
+    if (++column >= header_mapping_info[track_header].size()) {
+      warning(MYNAME ": too many fields in Track record!\n");
+      break;
+    }
+    const auto& [name, field_no] = header_mapping_info[track_header].at(column);
     switch (field_no) {
     case 1:
       if (!str.isEmpty()) {
@@ -1260,8 +1267,11 @@ parse_route_waypoint(const QStringList& lineparts)
   bind_fields(rtept_header);
 
   for (const auto& str : lineparts) {
-    column++;
-    int field_no = header_fields[rtept_header][column];
+    if (++column >= header_mapping_info[rtept_header].size()) {
+      warning(MYNAME ": too many fields in Route Waypoint record!\n");
+      break;
+    }
+    const auto& [name, field_no] = header_mapping_info[rtept_header].at(column);
     switch (field_no) {
     case 1:
       if (str.isEmpty()) {
@@ -1289,25 +1299,27 @@ parse_track_waypoint(const QStringList& lineparts)
   auto* wpt = new Waypoint;
 
   for (const auto& str : lineparts) {
-    column++;
+    if (++column >= header_mapping_info[trkpt_header].size()) {
+      warning(MYNAME ": too many fields in Trackpoint record!\n");
+      break;
+    }
     double x;
 
     if (str.isEmpty()) {
       continue;
     }
 
-    int field_no = header_fields[trkpt_header][column];
+    const auto& [name, field_no] = header_mapping_info[trkpt_header].at(column);
+
     switch (field_no) {
     case 1:
       parse_coordinates(str, datum_index, grid_index,
                         &wpt->latitude, &wpt->longitude, MYNAME);
       break;
-    case 2: {
-      QDateTime ct;
-      if (parse_date_and_time(str, &ct)) {
-        wpt->SetCreationTime(ct);
+    case 2:
+      if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+        wpt->SetCreationTime(dt);
       }
-    }
     break;
     case 3:
       if (parse_distance(str, &x, 1, MYNAME)) {
@@ -1342,11 +1354,13 @@ parse_track_waypoint(const QStringList& lineparts)
 static void
 garmin_txt_rd_init(const QString& fname)
 {
-  memset(&gtxt_flags, 0, sizeof(gtxt_flags));
+  gtxt_flags = {};
 
   fin = new gpsbabel::TextStream;
   fin->open(fname, QIODevice::ReadOnly, MYNAME, "windows-1252");
-  memset(&header_ct, 0, sizeof(header_ct));
+
+  free_headers();
+  header_column_names.clear();
 
   datum_index = -1;
   grid_index = (grid_type) -1;
@@ -1357,9 +1371,8 @@ garmin_txt_rd_init(const QString& fname)
 static void
 garmin_txt_rd_deinit()
 {
-  for (header_type h = waypt_header; h <= unknown_header; ++h) {
-    free_header(h);
-  }
+  free_headers();
+  header_column_names.clear();
   fin->close();
   delete fin;
   fin = nullptr;
diff --git a/reference/garmin_txt_reorder.txt b/reference/garmin_txt_reorder.txt
new file mode 100755 (executable)
index 0000000..1c49b6d
--- /dev/null
@@ -0,0 +1,96 @@
+Grid   Lat/Lon hddd°mm.mmm'\r
+Datum  WGS 84\r
+\r
+Header Description     Name    Type    Position        Altitude        Depth   Proximity       Temperature     Display Mode    Color   Symbol  Facility        City    State   Country Date Modified   Link    Categories\r
+\r
+Waypoint               001     Map Line        N50 29.556188732 E12 06.325848140                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               002     Map Intersection        N50 29.556188732 E12 06.325848140                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               003     Map Intersection        N50 29.656610638 E12 06.307823695                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               004     Map Line        N50 29.630036652 E12 06.366030984                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               005     Map Line        N50 29.630036652 E12 06.366030984                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               006     Map Intersection        N50 29.602537304 E12 06.426270045                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint               007     Map Line        N50 29.619586095 E12 06.429106481                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37\r
+Waypoint       Dummy airport (Germany) ED_X    Airport N51 53.627961650 E12 58.676564991                                       Symbol & Name   Unknown Airport FAC1    CITY1           Germany (civil) 28/03/2006 01:38:07\r
+Waypoint       Dummy airport (Spain)   GC_X    Airport N38 37.919719778 W3 10.443304181                                        Symbol & Name   Unknown Airport FAC2    CITY2           Spain (Canary Islands)  28/03/2006 01:42:01\r
+Waypoint       Jahnstrasse 11  Jahnstrasse     User Waypoint   N50 29.619998485 E12 06.429000869                                       Symbol & Description    Unknown Flag, Red                                       31/03/2006 21:48:22\r
+Waypoint       Dummy airport (France)  LF_X    Airport N46 23.256332763 E3 29.896638617                                        Symbol & Name   Unknown Airport FAC3    CITY3           France (Metropolitan France; including Saint-Pierre and Miquelon)       28/03/2006 01:40:32\r
+Waypoint       Dummy airport (Italy)   LI_X    Airport N43 18.873018846 E12 09.693240859                                       Symbol & Name   Unknown Heliport        FAC4    CITY4           Italy   28/03/2006 01:43:25\r
+Waypoint       Liebknechtstrasse 90    Liebknechtstrasse       User Waypoint   N50 29.630041681 E12 06.366015896                                       Symbol & Name   Unknown Waypoint                                        31/03/2006 21:49:30\r
+Waypoint       Start   NARVA   User Waypoint   N50 29.556958191 E12 06.326884143       391 m                           Symbol  Unknown Flag, Green                                     31/03/2006 21:49:26     http://www.narva-light.de       Category 15\r
+\r
+\r
+Header Length  Name    Course  Waypoints       Link\r
+\r
+Route  4087 km ED_X-LF_X       232° true       4 waypoints\r
+\r
+Header Distance        Waypoint Name   Leg Length      Course\r
+\r
+Route Waypoint 0 m     ED_X\r
+Route Waypoint 1936 km GC_X    1936 km 227° true\r
+Route Waypoint 3323 km LI_X    1388 km 63° true\r
+Route Waypoint 4087 km LF_X    764 km  300° true\r
+\r
+Route  394 m   NARVA to Jahnstrasse    46° true        10 waypoints\r
+\r
+Header Distance        Waypoint Name   Leg Length      Course\r
+\r
+Route Waypoint 0 m     NARVA\r
+Route Waypoint 2 m     001     2 m     221° true\r
+Route Waypoint 2 m     002     0 m     0° true\r
+Route Waypoint 189 m   003     188 m   353° true\r
+Route Waypoint 274 m   004     85 m    126° true\r
+Route Waypoint 274 m   Liebknechtstrasse       0 m     298° true\r
+Route Waypoint 274 m   005     0 m     118° true\r
+Route Waypoint 361 m   006     88 m    126° true\r
+Route Waypoint 393 m   007     32 m    6° true\r
+Route Waypoint 394 m   Jahnstrasse     1 m     351° true\r
+\r
+\r
+Header Start Time      Name    Elapsed Time    Length  Average Speed   Link\r
+\r
+Track  01/05/2005 15:02:47     ACTIVE LOG 006  0:33:09 653 m   1.2 kph\r
+\r
+Header Time    Position        Altitude        Depth   Temperature     Leg Length      Leg Time        Leg Speed       Leg Course\r
+\r
+Trackpoint     01/05/2005 15:02:47     N51 18.776294924 E12 24.789910130       161 m   0.0 m\r
+Trackpoint     01/05/2005 15:03:25     N51 18.772960603 E12 24.794909097       154 m   0.0 m           8 m     0:00:38 0.8 kph 137° true\r
+Trackpoint     01/05/2005 15:03:39     N51 18.771295957 E12 24.794909097       148 m   0.0 m           3 m     0:00:14 0.8 kph 180° true\r
+Trackpoint     01/05/2005 15:04:16     N51 18.769626282 E12 24.798243418       139 m   0.0 m           5 m     0:00:37 0.5 kph 129° true\r
+Trackpoint     01/05/2005 15:05:02     N51 18.769626282 E12 24.796573743       145 m   0.0 m           2 m     0:00:46 0.2 kph 270° true\r
+Trackpoint     01/05/2005 15:05:45     N51 18.769626282 E12 24.798243418       134 m   0.0 m           2 m     0:00:43 0.2 kph 90° true\r
+Trackpoint     01/05/2005 15:06:44     N51 18.766296990 E12 24.799908064       131 m   0.0 m           6 m     0:00:59 0.4 kph 163° true\r
+Trackpoint     01/05/2005 15:07:50     N51 18.766296990 E12 24.799908064       130 m   0.0 m           0 m     0:01:06 0 kph   0° true\r
+Trackpoint     01/05/2005 15:08:19     N51 18.764627315 E12 24.799908064       132 m   0.0 m           3 m     0:00:29 0.4 kph 180° true\r
+Trackpoint     01/05/2005 15:11:16     N51 18.767961636 E12 24.798243418       144 m   0.0 m           6 m     0:02:57 0.1 kph 343° true\r
+Trackpoint     01/05/2005 15:12:34     N51 18.774630278 E12 24.806576706       147 m   0.0 m           16 m    0:01:18 0.7 kph 38° true\r
+Trackpoint     01/05/2005 15:13:18     N51 18.779629245 E12 24.828242250       145 m   0.0 m           27 m    0:00:44 2 kph   70° true\r
+Trackpoint     01/05/2005 15:13:27     N51 18.782963566 E12 24.828242250       145 m   0.0 m           6 m     0:00:09 2 kph   0° true\r
+Trackpoint     01/05/2005 15:13:37     N51 18.782963566 E12 24.829906896       135 m   0.0 m           2 m     0:00:10 0.7 kph 90° true\r
+Trackpoint     01/05/2005 15:13:46     N51 18.786292858 E12 24.829906896       135 m   0.0 m           6 m     0:00:09 2 kph   0° true\r
+Trackpoint     01/05/2005 15:14:03     N51 18.792961501 E12 24.833241217       136 m   0.0 m           13 m    0:00:17 3 kph   17° true\r
+Trackpoint     01/05/2005 15:14:16     N51 18.797960468 E12 24.838240184       135 m   0.0 m           11 m    0:00:13 3 kph   32° true\r
+Trackpoint     01/05/2005 15:14:26     N51 18.796295822 E12 24.843239151       139 m   0.0 m           7 m     0:00:10 2 kph   118° true\r
+Trackpoint     01/05/2005 15:14:30     N51 18.796295822 E12 24.846573472       139 m   0.0 m           4 m     0:00:04 3 kph   90° true\r
+Trackpoint     01/05/2005 15:15:06     N51 18.782963566 E12 24.876572303       141 m   0.0 m           43 m    0:00:36 4 kph   125° true\r
+Trackpoint     01/05/2005 15:15:27     N51 18.777959570 E12 24.889909588       140 m   0.0 m           18 m    0:00:21 3 kph   121° true\r
+Trackpoint     01/05/2005 15:15:39     N51 18.774630278 E12 24.896573201       140 m   0.0 m           10 m    0:00:12 3 kph   129° true\r
+Trackpoint     01/05/2005 15:25:31     N51 18.776294924 E12 24.898242876       152 m   0.0 m           4 m     0:09:52 0.0 kph 32° true\r
+Trackpoint     01/05/2005 15:25:40     N51 18.776294924 E12 24.898242876       152 m   0.0 m           0 m     0:00:09 0 kph   0° true\r
+Trackpoint     01/05/2005 15:29:18     N51 18.777959570 E12 24.896573201       155 m   0.0 m           4 m     0:03:38 0.1 kph 328° true\r
+Trackpoint     01/05/2005 15:30:30     N51 18.789627180 E12 24.874907658       149 m   0.0 m           33 m    0:01:12 2 kph   311° true\r
+Trackpoint     01/05/2005 15:30:37     N51 18.789627180 E12 24.873243012       150 m   0.0 m           2 m     0:00:07 1.0 kph 270° true\r
+Trackpoint     01/05/2005 15:30:47     N51 18.789627180 E12 24.866574369       151 m   0.0 m           8 m     0:00:10 3 kph   270° true\r
+Trackpoint     01/05/2005 15:30:48     N51 18.789627180 E12 24.863240048       151 m   0.0 m           4 m     0:00:01 14 kph  270° true\r
+Trackpoint     01/05/2005 15:30:52     N51 18.799630143 E12 24.834905863       150 m   0.0 m           38 m    0:00:04 34 kph  299° true\r
+Trackpoint     01/05/2005 15:30:57     N51 18.821295686 E12 24.799908064       150 m   0.0 m           57 m    0:00:05 41 kph  315° true\r
+Trackpoint     01/05/2005 15:31:03     N51 18.839626908 E12 24.771573879       150 m   0.0 m           47 m    0:00:06 28 kph  316° true\r
+Trackpoint     01/05/2005 15:31:10     N51 18.852959163 E12 24.749908336       150 m   0.0 m           35 m    0:00:07 18 kph  315° true\r
+Trackpoint     01/05/2005 15:32:38     N51 18.877959028 E12 24.573239610       143 m   0.0 m           210 m   0:01:28 9 kph   283° true\r
+Trackpoint     01/05/2005 15:32:45     N51 18.877959028 E12 24.569910318       141 m   0.0 m           4 m     0:00:07 2 kph   270° true\r
+Trackpoint     01/05/2005 15:33:17     N51 18.877959028 E12 24.569910318       143 m   0.0 m           0 m     0:00:32 0 kph   0° true\r
+Trackpoint     01/05/2005 15:33:42     N51 18.877959028 E12 24.566575997       139 m   0.0 m           4 m     0:00:25 0.6 kph 270° true\r
+Trackpoint     01/05/2005 15:33:54     N51 18.877959028 E12 24.561572000       139 m   0.0 m           6 m     0:00:12 2 kph   270° true\r
+Trackpoint     01/05/2005 15:34:04     N51 18.877959028 E12 24.561572000       138 m   0.0 m           0 m     0:00:10 0 kph   0° true\r
+Trackpoint     01/05/2005 15:34:20     N51 18.877959028 E12 24.561572000       139 m   0.0 m           0 m     0:00:16 0 kph   0° true\r
+Trackpoint     01/05/2005 15:35:45     N51 18.877959028 E12 24.561572000       144 m   0.0 m           0 m     0:01:25 0 kph   0° true\r
+Trackpoint     01/05/2005 15:35:56     N51 18.877959028 E12 24.561572000       145 m   0.0 m           0 m     0:00:11 0 kph   0° true\r
index 934bd7f09689e1ccb68e6185f14484bff467b4ce..5f3de9518a914166cb762e58454b002426fc1a5a 100644 (file)
@@ -13,3 +13,10 @@ gpsbabel -i garmin_txt -f ${TMPDIR}/garmin_txt-2.txt -o garmin_txt,prec=9 -F ${T
 # test can fail because of localtime/gmtime differences
 # 
 ## compare ${TMPDIR}/garmin_txt-2.txt ${TMPDIR}/garmin_txt-3.txt
+
+# garmin_txt_reorder has columsn 2 and 3 interchanged compared to garmin_txt.txt.
+# The reader should bind the headers so the writer gets the correct data.
+gpsbabel -i garmin_txt -f ${REFERENCE}/garmin_txt.txt -o garmin_txt,prec=9 -F ${TMPDIR}/garmin_txt-4.txt
+gpsbabel -i garmin_txt -f ${REFERENCE}/garmin_txt_reorder.txt -o garmin_txt,prec=9 -F ${TMPDIR}/garmin_txt-5.txt
+compare ${REFERENCE}/garmin_txt.txt ${TMPDIR}/garmin_txt-4.txt
+compare ${REFERENCE}/garmin_txt.txt ${TMPDIR}/garmin_txt-5.txt